/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.service.datascope;

import vip.dexian.admin.dto.AdminPrincipal;
import vip.dexian.admin.dto.AdminRoleDTO;
import vip.dexian.admin.enums.DataScopeEnum;
import vip.dexian.admin.service.RoleDepartmentService;
import vip.dexian.common.query.BaseQueryable;
import vip.dexian.common.query.Filter;
import vip.dexian.common.query.Listable;
import vip.dexian.common.query.Pageable;
import vip.dexian.common.utils.NumberUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 数据权限切面
 *
 * @author 挺好的 2023年06月08日 16:02
 */
@Aspect
@Component
@Slf4j
public class DataScopeAspect {

    /**
     * 角色关联的部门
     */
    @Resource (name = "roleDepartmentServiceImpl")
    private RoleDepartmentService roleDepartmentService;

    /**
     * 定义一个前置通知，为方法的执行补充一个搜索条件， @DataScope注解应用在Service上
     *
     * @param joinPoint
     *         {@link org.aspectj.lang.JoinPoint}
     * @param dataScope
     *         {@link DataScope}
     */
    @Before ("@annotation(dataScope)")
    public void doBefore (JoinPoint joinPoint, DataScope dataScope) {

        AdminPrincipal adminPrincipal = (AdminPrincipal) SecurityContextHolder.getContext().getAuthentication()
                .getPrincipal();

        Object arg = joinPoint.getArgs()[0];

        if (arg instanceof Pageable pageable) {
            this.addFilter(adminPrincipal, pageable.getFilters(), dataScope, joinPoint);
        }

        if (arg instanceof Listable listable) {
            this.addFilter(adminPrincipal, listable.getFilters(), dataScope, joinPoint);
        }

    }

    /**
     * 添加filter
     *
     * @param adminPrincipal
     *         管理员
     * @param filters
     *         过滤条件
     */
    private void addFilter (AdminPrincipal adminPrincipal, List <Filter> filters, DataScope dataScope,
            JoinPoint joinPoint) {
        List <AdminRoleDTO> roles = adminPrincipal.getRoles();

        List <Long> departmentIds = this.getDepartmentIds(roles, adminPrincipal, joinPoint, dataScope);

        if (CollectionUtils.isEmpty(departmentIds)) {
            return;
        }

        // 如果只有1个元素，则直接使用 = 查询
        if (departmentIds.size() == 1) {
            filters.add(Filter.eq(dataScope.alias(), departmentIds.get(0)));
        } else {
            filters.add(Filter.in(dataScope.alias(), departmentIds));
        }

    }

    /**
     * 获取部门id
     *
     * @param roles
     *         角色
     * @param adminPrincipal
     *         当前登录用户
     *
     * @return 部门id列表
     */
    private List <Long> getDepartmentIds (List <AdminRoleDTO> roles, AdminPrincipal adminPrincipal, JoinPoint joinPoint,
            DataScope dataScope) {

        DataScopeEnum dataScopeEnum = adminPrincipal.getDataScope();

        log.debug("当前数据权限：{}", dataScopeEnum);

        // 全部
        if (DataScopeEnum.ALL.equals(dataScopeEnum)) {
            return Collections.emptyList();
        }

        // 自定义
        if (DataScopeEnum.CUSTOM.equals(dataScopeEnum)) {
            //对于自定义的，获取到全部的部门ID
            List <Long> roleIds = roles.stream().map(AdminRoleDTO::getRoleId).distinct().sorted().toList();
            List <Long> departmentIds = this.roleDepartmentService.findDepartmentIds(roleIds);

            return this.reBuildDepartmentIds(departmentIds, joinPoint, dataScope);
        }

        // 本级
        if (DataScopeEnum.SAME_LEVEL.equals(dataScopeEnum)) {
            // 本级的，清空客户端的查询权限
            this.clearDataScope(joinPoint, dataScope);
            return this.buildDepartmentIdList(adminPrincipal.getDepartmentId());
        }

        return null;
    }


    /**
     * @param joinPoint
     *         {@link JoinPoint}
     * @param dataScope
     *         {@link DataScope}
     *
     * @return 客户端提交的部门id
     */
    private List <Long> reBuildDepartmentIds (List <Long> departmentIds, JoinPoint joinPoint, DataScope dataScope) {

        BaseQueryable queryable = this.getQueryable(joinPoint);

        // 如果没有找到查询参数
        if (queryable == null) {
            return departmentIds;
        }

        Long requestDepartmentId = NumberUtils.toLong(queryable.getValue(dataScope.alias()));

        // 如果没有传递参数或者参数非法
        if (!NumberUtils.isPositive(requestDepartmentId)) {
            return departmentIds;
        }

        // 移除原来的搜索条件
        queryable.remove(dataScope.alias());

        if (departmentIds.contains(requestDepartmentId)) {
            return this.buildDepartmentIdList(requestDepartmentId);
        }

        return departmentIds;
    }

    /**
     * 创建部门id列表
     *
     * @param id
     *         部门id
     *
     * @return 部门id列表
     */
    private List <Long> buildDepartmentIdList (Long id) {

        if (!NumberUtils.isPositive(id)) {
            return Collections.emptyList();
        }

        List <Long> ids = new ArrayList <>();

        ids.add(id);

        return ids;

    }

    /**
     * 获取queryable 对象
     *
     * @param joinPoint
     *         {@link JoinPoint}
     *
     * @return {@link BaseQueryable}
     */
    private BaseQueryable getQueryable (JoinPoint joinPoint) {
        // 获取第一个参数
        Object arg = joinPoint.getArgs()[0];

        if (arg instanceof Pageable pageable) {
            return pageable;
        }

        if (arg instanceof Listable listable) {
            return listable;
        }

        return null;
    }

    /**
     * 清除原来的数据权限
     *
     * @param joinPoint
     *         {@link JoinPoint}
     */
    private void clearDataScope (JoinPoint joinPoint, DataScope dataScope) {

        BaseQueryable queryable = this.getQueryable(joinPoint);

        if (queryable == null) {
            return;
        }

        queryable.remove(dataScope.alias());
    }
}
